﻿using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Colorful.Cache
{
    /// <summary>
    /// Redis缓存封装
    /// </summary>
    public class RedisCache : IDisposable
    {
        private ConnectionMultiplexer _redis;
        private IDatabase _db;
        private ISubscriber _subscriber;
        private string _prefix;

        #region 初始化
        public RedisCache()
            :this(CacheSetting.Prefix, CacheSetting.ConnectionString)
        {

        }
        /// <summary>
        /// 初始化RedisCacheHelper
        /// </summary>
        /// <param name="prefix">用于存储Key的前辍（必填）</param>
        public RedisCache(string prefix)
            : this(prefix, "localhost:6379,syncTimeout=5000")
        {
        }
        /// <summary>
        /// 初始化RedisCacheHelper
        /// </summary>
        /// <param name="prefix">缓存前辍如：colorful</param>
        /// <param name="connectionString">连接字符串，如：localhost:6379,password=xxx</param>
        public RedisCache(string prefix, string connectionString)
        {
            _prefix = prefix;
            _redis = ConnectionMultiplexer.Connect(connectionString);
            _db = _redis.GetDatabase();
        }
        #endregion

        #region 属性
        public ConnectionMultiplexer Redis
        {
            get
            {
                return _redis;
            }
        }
        public IDatabase Db
        {
            get
            {
                return _db;
            }
        }
        public ISubscriber Subscriber
        {
            get
            {
                if (_subscriber == null)
                    _subscriber = _redis.GetSubscriber();
                return _subscriber;
            }
        }
        #endregion

        #region 缓存方法
        public bool Exists(string key)
        {
            key = this.GetKey(key);
            return _db.KeyExists(key);
        }
        public Task<bool> ExistsAsync(string key)
        {
            key = this.GetKey(key);
            return _db.KeyExistsAsync(key);
        }

        public async void Set<T>(string key, T value, TimeSpan? expiry = default(TimeSpan?))
        {
            key = this.GetKey(key);
            await SetAsync(key, value, expiry);
            //return _db.StringSet(key, JsonHelper.ToJson(value), expiry);
        }

        public Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiry = default(TimeSpan?))
        {
            key = this.GetKey(key);
            return _db.StringSetAsync(key, JsonHelper.ToJson(value), expiry);
        }

        public async void Remove(string key)
        {
            key = this.GetKey(key);
            await _db.KeyDeleteAsync(key);
        }

        public Task<bool> RemoveAsync(string key)
        {
            key = this.GetKey(key);
            return _db.KeyDeleteAsync(key);
        }

        public T Get<T>(string key, Func<T> requireData = null, TimeSpan? expiry = default(TimeSpan?))
        {
            key = this.GetKey(key);
            var value = _db.StringGet(key);
            if (string.IsNullOrEmpty(value))
            {
                if (requireData != null)
                {
                    var data = requireData();
                    _db.StringSet(key, JsonHelper.ToJson(data), expiry);
                    return data;
                }
            }
            else
            {
                return JsonHelper.Parse<T>(value);
            }
            return default(T);
        }

        public async Task<T> GetAsync<T>(string key, Func<T> requireData = null, TimeSpan? expiry = default(TimeSpan?))
        {
            key = this.GetKey(key);
            var data = await _db.StringGetAsync(key);
            if (data.HasValue)
                return JsonHelper.Parse<T>(data.ToString());
            else if (requireData != null)
            {
                var result = requireData();
                if (result != null)
                {
                    await _db.StringSetAsync(key, JsonHelper.ToJson(result));
                }
            }
            return default(T);
        }
        #endregion

        #region 订阅方法

        #region 发布
        public long Publish<T>(string channel, T value)
        {
            return this.Subscriber.Publish(new RedisChannel(channel, RedisChannel.PatternMode.Auto), JsonHelper.ToJson(value));
        }
        public long Publish(string channel, string value)
        {
            return this.Subscriber.Publish(new RedisChannel(channel, RedisChannel.PatternMode.Auto), value);
        }
        public Task<long> PublishAsync<T>(string channel, T value)
        {
            return this.Subscriber.PublishAsync(new RedisChannel(channel, RedisChannel.PatternMode.Auto), JsonHelper.ToJson(value));
        }
        public Task<long> PublishAsync(string channel, string value)
        {
            return this.Subscriber.PublishAsync(new RedisChannel(channel, RedisChannel.PatternMode.Auto), value);
        }
        #endregion

        #region 订阅
        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="channel">要订阅的频道</param>
        /// <param name="messageHandler">消息回调</param>
        public RedisChannel Subscribe(string channel, Action<string> messageHandler, CommandFlags flags = CommandFlags.None)
        {
            var redisChannel = new RedisChannel(channel, RedisChannel.PatternMode.Auto);
            this.Subscriber.Subscribe(redisChannel, (ch, value) =>
            {
                messageHandler(value);
            }, flags);
            return redisChannel;
        }
        public Task<RedisChannel> SubscribeAsync(string channel, Action<string> messageHandler, CommandFlags flags = CommandFlags.None)
        {
            var redisChannel = new RedisChannel(channel, RedisChannel.PatternMode.Auto);
            var task = this.Subscriber.SubscribeAsync(redisChannel, (ch, value) =>
            {
                messageHandler(value);
            }, flags);
            return Task.FromResult<RedisChannel>(redisChannel);
        }
        #endregion

        #region 取消订阅
        public void Unsubscribe(RedisChannel channel, Action<string> messageHandler, CommandFlags flags = CommandFlags.None)
        {
            this.Subscriber.Unsubscribe(channel, (ch, value) =>
            {
                messageHandler(value);
            }, flags);
        }
        public void UnsubscribeAll(CommandFlags flags = CommandFlags.None)
        {
            this.Subscriber.UnsubscribeAll(flags);
        }
        #endregion

        #endregion

        #region 消息列队

        #region LeftPush
        /// <summary>
        /// 将值插入到已存在的列表头部
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async void LeftPush<T>(string key, T value)
        {
            key = GetKey(key);
            await _db.ListLeftPushAsync(key, JsonHelper.ToJson(value));
        }
        public async void LeftPush(string key, string value)
        {
            key = GetKey(key);
            await _db.ListLeftPushAsync(key, value);
        }
        /// <summary>
        /// 将值插入到已存在的列表尾部
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async void RightPush<T>(string key , T value)
        {
            key = GetKey(key);
            await _db.ListRightPushAsync(key, JsonHelper.ToJson(value));
        }
        public async void RightPush(string key, string value)
        {
            key = GetKey(key);
            await _db.ListRightPushAsync(key, value);
        }
        /// <summary>
        /// 移除并返回列表的第一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> LeftPop<T>(string key)
        {
            key = GetKey(key);
            var result = await _db.ListLeftPopAsync(key);
            if (result.HasValue)
                return JsonHelper.Parse<T>(result);
            return default(T);
        }
        /// <summary>
        /// 移除并返回列表的最后一个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> RightPop<T>(string key)
        {
            key = GetKey(key);
            var result = await _db.ListRightPopAsync(key);
            if (result.HasValue)
                return JsonHelper.Parse<T>(result);
            return default(T);
        }
        /// <summary>
        /// 获取列表长度
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public long ListLength(string key)
        {
            key = GetKey(key);
            return _db.ListLength(key);
        }
        /// <summary>
        /// 异步获取列表长度
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Task<long> ListLengthAsync(string key)
        {
            key = GetKey(key);
            return _db.ListLengthAsync(key);
        }
        #endregion

        #endregion

        #region Dispose
        public void Dispose()
        {
            _redis.Dispose();
        }
        #endregion

        #region 私有方法
        private string GetKey(string key)
        {
            if (string.IsNullOrEmpty(_prefix))
                return key;
            return string.Format("{0}_{1}", _prefix, key);
        }
        #endregion
    }
}